#pragma once 
#include <iostream>
#include <string>
#include <memory>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sstream>
#include <fstream>
#include <filesystem> // C++17编译器才支持

#include "Mutex.hpp"

namespace LogMoudle
{
    // [可读性很好的时间] [⽇志等级] [进程pid] [打印对应⽇志的⽂件名][⾏号] - 消息内容，⽀持可变参数
    // [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world

    // 1. 构成 日志存放的位置
    const char *default_logpath = "./log/";
    const char *default_logfile = "log.txt";

    enum class LogLevel
    {
        NORMAL = 0,
        DEBUG,
        WARNNING,
        ERROR,
        FATAL,
    };

    std::string LevelToString(LogLevel &level)
    {
        switch (level)
        {
        case LogLevel::NORMAL:
            return "NORMAL";
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::WARNNING:
            return "WARNNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "None";
        }
    }
    std::string GetCurrent()
    {
        time_t time_stamp = ::time(nullptr);
        struct tm curr;                  // tm 是系统自带的关于日期时间的结构体
        localtime_r(&time_stamp, &curr); // 获取当前日期时间
        char buffer[1024];
        snprintf(buffer, sizeof(buffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 curr.tm_year + 1900, curr.tm_mon + 1, curr.tm_mday,
                 curr.tm_hour, curr.tm_min, curr.tm_sec);
        return buffer;
    }
    // 日志策略
    class LogStrategy
    {
        // 将析构函数设为虚函数，不会因为派生类而造成内存泄漏
    public:
        virtual ~LogStrategy() = default;
        virtual void SyncLog(const std::string &str) = 0;
    };

    // 具体日志策略实现
    // 控制台策略
    class LogConsoleStrategy : public LogStrategy
    {
    public:
        void SyncLog(const std::string &str) override
        {
            MutexMoudle::LockGuard lock(_mutex);
            std::cout << str << std::endl;
        }
        ~LogConsoleStrategy(){}

    private:
        MutexMoudle::Mutex _mutex; // 显示器也是一个共享资源
    };
    // 文件级（磁盘级）策略
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string& logpath=default_logpath,const std::string& logfile=default_logfile )
        :_logpath(logpath),_logfile(logfile)
        {
            if(!std::filesystem::exists(_logpath))
            {
                try
                {
                    std::filesystem::create_directory(_logpath); 
                }
                catch(const std::filesystem::filesystem_error& e)
                {
                    std::cerr<<e.what()<<std::endl;
                }
            }
        }
        void SyncLog(const std::string &str) override
        {
            MutexMoudle::LockGuard lock (_mutex);
            const std::string log=_logpath+_logfile;
            std::ofstream out(log,std::ios::app);
            if(!out.is_open()) return; 
            out<<str<<'\n';
            out.close();
            
        }
        ~FileLogStrategy(){}
    private:
        std::string _logpath;
        std::string _logfile;
        MutexMoudle::Mutex _mutex;   // 文件也是公共资源，也需要保护互斥
    };

    // 具体的日志实现
    // 日志类: 构建日志字符串, 根据策略，进行刷新
    class Logger
    {
    public:
        Logger()
        {
            // 默认采用屏幕刷新
            _Strategy = std::make_shared<LogConsoleStrategy>();
        }
        void EnableLogConsoleStrategy()
        {
            _Strategy = std::make_shared<LogConsoleStrategy>();
        }
        void EnableFileLogStrategy()
        {
            _Strategy = std::make_shared<FileLogStrategy>();
        }
        ~Logger() {}
        class LogMessage
        {
            // 采用RAII风格，一个对象表示一条完整的日志语句，销毁时刷新
        public:
            LogMessage(LogLevel level, const std::string& filename, int line, Logger &logger)
                : _time(GetCurrent()),
                  _level(level),
                  _pid(getpid()),
                  _filename(filename),
                  _line(line),
                  _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _time << "]" << "[" << LevelToString(_level) << "]" << "[" << _pid << "]" << "[" << _filename << "]"
                   << "[" << _line << "] ";
                _loginfo = ss.str();
            }
            // 加上模版，适用于任何类型
            template <class T>
            LogMessage &operator<<( const T &info)
            {
                std::stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
            }
            ~LogMessage()
            {
                _logger._Strategy->SyncLog(_loginfo);
            }

        private:
            std::string _time;
            LogLevel _level;
            pid_t _pid;
            std::string _filename;
            int _line;
            std::string _loginfo; // 最后输出的日志语句

            Logger &_logger; // 负责最后析构时使用策略
        };
        // 临时变量/匿名变量的底层实际就是调用拷贝构造，形成一个临时变量
        // 故意写拷贝构造，让其形成临时变量，后续在被<<的时候，可以持续引用。
        //  直到语句结束，才会自动析构临时LogMessage,至此也完成了日志的显示和刷新。
        //  同时形成的临时变量，内部也有独立的日志数据。
        LogMessage operator()(LogLevel level, const char *filename, int line)
        {
            return LogMessage(level, filename, line, *this);
        }

    private:
        std::shared_ptr<LogStrategy> _Strategy; // 日志所采用的策略
    };

    Logger logger;
#define LOG(level)  logger(level,__FILE__,__LINE__)
#define ENABLE_CONSOLE_LOG logger.EnableLogConsoleStrategy()
#define ENABLE_FILE_LOG logger.EnableFileLogStrategy()
}